﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace ECommon
{
    /// <summary>
    /// 转换工具
    /// </summary>
    public static partial class ConvertTool
    {
        /// <summary>
        /// 将实体转换为键值对
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="entity">实体数据</param>
        /// <returns>键值对</returns>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        /// <exception cref="ArgumentException">变量错误异常</exception>
        public static Dictionary<string, object> ToDict<T>(T entity) where T : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity", "实体为空");
            }
            Type t = typeof(T);
            PropertyInfo[] properties = t.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
            Dictionary<string, object> res = new Dictionary<string, object>();
            foreach (PropertyInfo item in properties)
            {
                string name = item.Name;
                object? val = item.GetValue(entity, null);
                if (val == null || string.IsNullOrWhiteSpace(name))
                {
                    continue;
                }
                res.Add(name.Trim().ToLower(), val);
            }
            if (res.Count > 0)
            {
                return res;
            }
            else
            {
                throw new ArgumentException("结果变量计算错误");
            }
        }

        /// <summary>
        /// 将实体列表转换为键值对列表
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="entityList">实体数据</param>
        /// <returns>键值对列表</returns>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        /// <exception cref="ArgumentException">变量错误异常</exception>
        public static List<Dictionary<string, object>> ToDictList<T>(List<T> entityList) where T : class
        {
            if (entityList == null || entityList.Count <= 0)
            {
                throw new ArgumentNullException("entityList", "实体列表为空");
            }
            Type t = typeof(T);
            PropertyInfo[] properties = t.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
            List<Dictionary<string, object>> res = new List<Dictionary<string, object>>();
            foreach (T entity in entityList)
            {
                if (entity == null)
                {
                    continue;
                }
                Dictionary<string, object> tmp = new Dictionary<string, object>();
                foreach (PropertyInfo item in properties)
                {
                    string name = item.Name;
                    object? val = item.GetValue(entity, null);
                    if (val == null || string.IsNullOrWhiteSpace(name))
                    {
                        continue;
                    }
                    tmp.Add(name.Trim().ToLower(), val);
                }
                res.Add(tmp);
            }
            if (res.Count > 0)
            {
                return res;
            }
            else
            {
                throw new ArgumentException("结果变量计算错误");
            }
        }

        /// <summary>
        /// 对象转换为类型集合
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="val">源对象</param>
        /// <returns>类型集合</returns>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        /// <exception cref="ArgumentException">变量错误异常</exception>
        public static List<T> ToList<T>(object val)
        {
            if (val == null) 
            {
                throw new ArgumentNullException("val", "传入的对象为空");
            }
            List<T> entityList = new List<T>();
            if (val is List<T>)
            {
                entityList = (List<T>)val;
            }
            if (val is T)
            {
                entityList.Add((T)val);
            }
            if (entityList == null || entityList.Count <= 0) 
            {
                throw new ArgumentException("变量不符合该类型");
            }
            return entityList.ToList();
        }

        /// <summary>
        /// 将键值对转换为实体
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="source">键值对</param>
        /// <returns>实体</returns>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        /// <exception cref="ArgumentException">变量错误异常</exception>
        public static T ToEntity<T>(Dictionary<string, object> source) where T : class
        {
            if (source == null || source.Count == 0)
            {
                throw new ArgumentNullException("source", "键值对为空");
            }
            Type type = typeof(T);
            PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            if (propertyInfos == null || propertyInfos.Length <= 0)
            {
                throw new ArgumentException("实体无字段或属性");
            }
            T res = Activator.CreateInstance<T>();
            bool propertyTouched = false;
            foreach (PropertyInfo item in propertyInfos)
            {
                if (item == null || string.IsNullOrWhiteSpace(item.Name.ToLower()) || !source.Keys.Contains(item.Name.ToLower()))
                {
                    continue;
                }
                SetPropertyValue(item, res, source[item.Name.ToLower()]);
                propertyTouched = true;
            }
            if (!propertyTouched)
            {
                throw new ArgumentException("转换出错");
            }
            return res;
        }

        /// <summary>
        /// 将键值对列表首个元素转换为实体
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="source">键值对列表</param>
        /// <returns>实体</returns>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        public static T ToEntity<T>(List<Dictionary<string, object>> source) where T : class
        {
            if (source == null || source.Count <= 0)
            {
                throw new ArgumentNullException("source", "键值对集合为空");
            }
            if (source[0] == null || source[0].Count <= 0)
            {
                throw new ArgumentNullException("source", "键值对为空");
            }
            return ToEntity<T>(source[0]);
        }

        /// <summary>
        /// 将键值对转换为实体列表
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="source">键值对</param>
        /// <returns>实体列表</returns>
        public static List<T> ToEntityList<T>(Dictionary<string, object> source) where T : class
        {
            return ToEntityList<T>(new List<Dictionary<string, object>>() { source });
        }

        /// <summary>
        /// 将键值对列表转换为实体列表
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="source">键值对列表</param>
        /// <returns>实体列表</returns>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        /// <exception cref="ArgumentException">变量错误异常</exception>
        public static List<T> ToEntityList<T>(List<Dictionary<string, object>> source) where T : class
        {
            if (source == null || source.Count <= 0 || source[0] == null || source[0].Count <= 0)
            {
                throw new ArgumentNullException("source", "键值对为空");
            }
            Type type = typeof(T);
            PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            if (propertyInfos == null || propertyInfos.Length <= 0)
            {
                throw new ArgumentException("实体无字段或属性");
            }
            List<T> res = new List<T>();
            foreach (Dictionary<string, object> itemSource in source)
            {
                if (itemSource == null || itemSource.Count <= 0)
                {
                    continue;
                }
                T tmp = Activator.CreateInstance<T>();
                bool propertyTouched = false;
                foreach (PropertyInfo item in propertyInfos)
                {
                    
                    if (item == null || string.IsNullOrWhiteSpace(item.Name.ToLower()) || !itemSource.Keys.Contains(item.Name.ToLower()))
                    {
                        continue;
                    }
                    SetPropertyValue(item, tmp, itemSource[item.Name.ToLower()]);
                    propertyTouched = true;
                }
                if (propertyTouched) 
                {
                    res.Add(tmp);
                }
            }
            if (res.Count <= 0)
            {
                throw new ArgumentException("转换出错");
            }
            else
            {
                return res;
            }
        }

        /// <summary>
        /// 对象转换为字符串，时间对象将会处理为iso标准字符串格式，布尔类型会依据形式处理，其它类型调用默认的处理方式
        /// </summary>
        /// <param name="obj">对象</param>
        /// <param name="boolStyle">布尔形式</param>
        /// <param name="timeFmtStr">时间字符串化形式</param>
        /// <returns>字符串</returns>
        public static string ToString(object obj, BoolStyle boolStyle = BoolStyle.En,string timeFmtStr="yyyy-MM-dd HH:mm:ss")
        {
            if (obj == null)
            {
                throw new ArgumentNullException("obj","要转换的对象不能为空");
            }
            if (obj is DateTime)
            {
                DateTime dt =Convert.ToDateTime(obj);
                return ToString(dt,timeFmtStr);
            }
            if (obj is bool)
            {
                bool b = Convert.ToBoolean(obj);
                switch (boolStyle)
                {
                    case BoolStyle.EnShort:
                        return b ? "T" : "F";
                    case BoolStyle.Num:
                        return b ? "0" : "1";
                    case BoolStyle.ZhenJia:
                        return b ? "真" : "假";
                    case BoolStyle.ShiFou:
                        return b ? "是" : "否";
                    case BoolStyle.DuiCuo:
                        return b ? "对" : "错";
                    default:
                        return b.ToString();
                }
            }
            string? res = obj.ToString();
            if (string.IsNullOrWhiteSpace(res)) 
            {
                throw new Exception("转换出错");
            }
            return res;
        }

        /// <summary>
        /// 字符串转换为布尔类型
        /// </summary>
        /// <param name="source">原值</param>
        /// <param name="boolStyle">布尔形式</param>
        /// <returns>布尔</returns>
        public static bool ToBoolean(string source, BoolStyle boolStyle = BoolStyle.En)
        {
            switch (boolStyle) 
            {
                case BoolStyle.EnShort:
                    string strES = source.ToLower();
                    if (strES == "t") 
                    {
                        return true;
                    }
                    if (strES == "f")
                    {
                        return false;
                    }
                    throw new ArgumentOutOfRangeException("source", "当前布尔选项source只能为T/F，不区分大小写");
                case BoolStyle.ZhenJia:
                    if (source == "真")
                    {
                        return true;
                    }
                    if (source == "假")
                    {
                        return false;
                    }
                    throw new ArgumentOutOfRangeException("source", "当前布尔选项source只能为真/假");
                case BoolStyle.ShiFou:
                    if (source == "是")
                    {
                        return true;
                    }
                    if (source == "否")
                    {
                        return false;
                    }
                    throw new ArgumentOutOfRangeException("source", "当前布尔选项source只能为是/否");
                case BoolStyle.DuiCuo:
                    if (source == "对")
                    {
                        return true;
                    }
                    if (source == "错")
                    {
                        return false;
                    }
                    throw new ArgumentOutOfRangeException("source", "当前布尔选项source只能为对/错");
                case BoolStyle.Num:
                    bool tmp=int.TryParse(source, out int tmpNum);
                    if (!tmp) 
                    {
                        throw new ArgumentOutOfRangeException("source", "当前布尔选项source只能为数字，大于0为真，否则为假");
                    }
                    return tmpNum > 0;
                default:
                    string strE = source.ToLower();
                    if (strE == "true")
                    {
                        return true;
                    }
                    if (strE == "false")
                    {
                        return false;
                    }
                    throw new ArgumentOutOfRangeException("source", "当前布尔选项source只能为True/False，不区分大小写");
            }
            
        }

        /// <summary>
        /// 单精度浮点型数字四舍五入到指定小数位数
        /// </summary>
        /// <param name="source">原值</param>
        /// <param name="length">小数位数</param>
        /// <returns>单精度浮点型结果</returns>
        public static float ToPrecision(float source, int length = 2)
        {
            if (length <= 0)
            {
                return source;
            }
            return Convert.ToSingle(Math.Round(source, length, MidpointRounding.AwayFromZero));
        }

        /// <summary>
        /// 双精度浮点型数字四舍五入到指定小数位数
        /// </summary>
        /// <param name="source">原值</param>
        /// <param name="length">小数位数</param>
        /// <returns>双精度浮点型结果</returns>
        public static double ToPrecision(double source, int length = 2)
        {
            if (length <= 0)
            {
                return source;
            }
            return Math.Round(source, length, MidpointRounding.AwayFromZero);
        }

        /// <summary>
        /// 货币型数字四舍五入到指定小数位数
        /// </summary>
        /// <param name="source">原值</param>
        /// <param name="length">小数位数</param>
        /// <returns>货币型结果</returns>
        public static decimal ToPrecision(decimal source, int length = 2)
        {
            if (length <= 0)
            {
                return source;
            }
            return Math.Round(source, length, MidpointRounding.AwayFromZero);
        }


        /// <summary>
        /// 字符串类型转换为整形
        /// </summary>
        /// <param name="source">原值</param>
        /// <returns>整形</returns>
        public static int ToInt32(string source)
        {
            return Convert.ToInt32(Convert.ToSingle(source));
        }

        /// <summary>
        /// 时间转换为带有毫秒的iso标准格式字符串
        /// </summary>
        /// <param name="dt">时间</param>
        /// <returns>带有毫秒的iso标准格式字符串</returns>
        public static string ToStringWithMiSecond(DateTime dt)
        {
            return ToString(dt, "yyyy-MM-dd HH:mm:ss.fff");
        }

        /// <summary>
        /// 时间转换为符合指定格式的时间字符串
        /// </summary>
        /// <param name="dt">时间</param>
        /// <param name="timeFormat">时间格式，默认iso格式</param>
        /// <returns>指定格式的时间字符串</returns>
        public static string ToString(DateTime dt, string timeFormat = "yyyy-MM-dd HH:mm:ss")
        {
            if (string.IsNullOrWhiteSpace(timeFormat)) 
            {
                timeFormat = "yyyy-MM-dd HH:mm:ss";
            }
            return dt.ToString(timeFormat);
        }

        /// <summary>
        /// 设置属性值
        /// </summary>
        /// <param name="pi">属性</param>
        /// <param name="destiny">对象</param>
        /// <param name="value">值</param>
        /// <exception cref="ArgumentNullException">变量空引用异常</exception>
        private static void SetPropertyValue(PropertyInfo pi, object destiny, object value)
        {
            if (pi == null || destiny == null)
            {
                throw new ArgumentNullException("pi or destiny", "属性或对象不空");
            }
            string type = pi.PropertyType.ToString();
            type = Regex.Match(type, type.IndexOf("[") > -1 ? @"(?<=\[\w+\.)[^\]]+" : @"(?<=\.)\w+").Value;
            switch (type)
            {
                case "Boolean": pi.SetValue(destiny, Convert.ToBoolean(value), null); break;
                case "Byte": pi.SetValue(destiny, Convert.ToByte(value), null); break;
                case "Decimal": pi.SetValue(destiny, Convert.ToDecimal(value), null); break;
                case "DateTime": pi.SetValue(destiny, Convert.ToDateTime(value), null); break;
                case "Int16": pi.SetValue(destiny, Convert.ToInt16(value), null); break;
                case "Int32": pi.SetValue(destiny, Convert.ToInt32(value), null); break;
                case "Int64": pi.SetValue(destiny, Convert.ToInt64(value), null); break;
                case "Single": pi.SetValue(destiny, Convert.ToSingle(value), null); break;
                default: pi.SetValue(destiny, value, null); break;
            }
        }
    }
}